/*
Copyright 2009 - 2013 Peter Krajnc, SCREENWORKER

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

*/
package org.compactmvc.core
{
	import flash.display.DisplayObject;
	import flash.utils.describeType;
	import flash.utils.getDefinitionByName;
	import flash.utils.getQualifiedClassName;	
	import org.compactmvc.interfaces.ICommand;
	import org.compactmvc.interfaces.IController;
	import org.compactmvc.interfaces.IModel;
	import org.compactmvc.patterns.Controller;
	import org.compactmvc.patterns.Model;
	import org.compactmvc.viewpatterns.AbstractViewComponentModel;
	import org.compactmvc.vo.ControllerVO;
	import org.compactmvc.vo.NoteVO;
	
	/**
	 * The CompactMVC Core
	 * @author Peter Krajnc, Screenworker
	 * 
	 */	
	internal class Core
	{
		private var _controller:Array = new Array();
		/**
		 * constructor 
		 * @param enforcer
		 * 
		 */		
		public function Core()
		{	
			
		}
		
		/**
		 * registered a controller 
		 * @param controller
		 * 
		 */		
		public function registerController(controller:Controller):void
		{
			if( isControllerRegistered(controller.getName()) )
			{
				throw new Error('controller already registered: ' + controller.getName());
			}  
			
			_controller[ controller.getName() ] = new ControllerVO(controller);
			controller.registerHandler();
		}
		
		/**
		 * removes a controller 
		 * @param controller
		 * 
		 */		
		public function removeController(controller:Controller):void
		{
			if( !isControllerRegistered(controller.getName()) )
			{
				throw new Error('controller not registered: ' + controller.getName());
			}  
			_controller[  controller.getName()  ] = null;
			delete _controller[  controller.getName()  ];
		}
		
		/**
		 * retrieves a registered Controller by Name
		 * @param controllerName
		 * @return 
		 * 
		 */      	
		public function retrieveController(controllerName:String):Controller
		{	
			if( !isControllerRegistered(controllerName) )
			{
				log(controllerName, 'controller not registered: ' + controllerName);
				return null;
			}
			return ControllerVO(_controller[ controllerName ]).controller;
		}
		
		/**
		 * retrieves all registered Controllers 
		 * @return 
		 * 
		 */      	
		public function retrieveRegisteredControllers():Array
		{	
			var arr:Array = new Array();
			for each(var item:ControllerVO in _controller)
			{
				arr.push(item.controller);
			}
			return arr;
		}
		
		/**
		 * register command 
		 * @param controller
		 * @param notificationType
		 * @param commandClassRef
		 * 
		 */	
		public function registerCommand(controller:Controller, notificationType:String, commandClassRef:Class):void
		{
			if(ControllerVO(_controller[ controller.getName() ]).commands[ notificationType ] != null )
			{
				throw new Error('command already registered: ' + notificationType);
			}  
			ControllerVO(_controller[ controller.getName() ]).commands[notificationType] = commandClassRef;
		}
		
		/**
		 * removes a registered Command 
		 * @param controller
		 * @param notificationType
		 * 
		 */		
		public function removeCommand(controller:Controller, notificationType:String):void
		{
			if( ControllerVO(_controller[ controller.getName() ]).commands[notificationType] == null)
			{
				throw new Error('command not registered: ' + notificationType);
			}  
			ControllerVO(_controller[ controller.getName() ]).commands[notificationType] = null;
			delete ControllerVO(_controller[ controller.getName() ]).commands[notificationType]; 	
		}      
		
		/**
		 * retrieves a registered command class reference
		 * @param controller
		 * @param notificationType
		 * @return 
		 * 
		 */      	
		public function retrieveCommand(controllerName:String, notificationType:String):Class
		{	
			if(controllerName  == null)
			{
				throw new Error( 'controllerName not given!');	
			}
			
			if(ControllerVO(_controller[ controllerName ]).commands[notificationType] == null)
			{
				log(controllerName, 'command for notificationtype "' +  notificationType +'" not registered!');	
				
				return null;
			}
			return ControllerVO(_controller[ controllerName ]).commands[notificationType];
		}
		
		/**
		 * executes a command stateless and then removes the command
		 */  
		public function executeCommand(controllerName:String, note:NoteVO):void
		{
			var commandClassRef:Class = retrieveCommand(controllerName, note.type) as Class;
			if(commandClassRef == null)
				return;
			
			var commandInstance:ICommand = new commandClassRef( retrieveController(controllerName) );
			var controller:IController = retrieveController(controllerName);
			if(commandInstance && commandInstance is ICommand && controller && controller is IController)
			{	
				//dependency Injection
				propertyInjection(commandClassRef, _controller[ controllerName ], commandInstance);
				
				//execution
				commandInstance.execute(note);
				
				//destroy
				commandInstance = null;		
			}
		}
		
		/**
		 *registers a View Model withe the controller 
		 * @param controller
		 * @param modelName
		 * @param modelClassRef
		 * @param viewComponent
		 * 
		 */
		public function registerViewModel(controller:Controller, modelName:String, modelClassRef:Class, viewComponent:Object):void
		{
			if(!controller)
			{
				throw new Error('registerViewModel Error: controller not registered!');				
			}	
			if(isModelRegistered(controller.getName(), modelName) )
			{	
				throw new Error('registerViewModel Error: model already registered: ' + modelName + ' modelClassRef:' +  getQualifiedClassName(modelClassRef));				
			}
			if(modelClassRef.NAME != modelName)
			{
				throw new Error('registerViewModel Error: name of modelClassRef did not match with modelName! The modelClassRef or modelName is wrong!: modelName: ' + modelName + ' modelClassRef:' +  getQualifiedClassName(modelClassRef));		
			}
			
			var viewModel:Object =  new modelClassRef(modelName, controller, viewComponent);	
			
			if(viewModel is AbstractViewComponentModel && viewModel is IModel)
			{
				ControllerVO(_controller[ controller.getName() ]).models[ modelName ] = viewModel;
				
				//start dependency Injection   
				dependencyInjection(_controller[ controller.getName() ] as ControllerVO);
				
				//call registration method
				ControllerVO(_controller[ controller.getName() ]).models[ modelName ].registerHandler();
				
				//notify Application that model has registered
				controller.sendNotification(new NoteVO(Controller.MODEL_REGISTER, null, modelName, viewModel) );
				
				log(controller.getName(), 'View Model registered: ' +  modelName + ' ClassReference: ' + modelClassRef + ' to Controller: ' + controller.getName() );		
			}
			else
			{
				throw new Error('invalid modelClassRef!');
			}
		}
		
		/**
		 * removes the view model 
		 * @param controller
		 * @param modelName
		 * 
		 */	
		public function removeViewModel(controller:Controller, modelName:String):void
		{
			removeViewModel(controller, modelName);
		}
		
		/**
		 * registers a model
		 * @param controller
		 * @param modelName
		 * @param modelClassRef
		 * 
		 */		
		public function registerModel(controller:Controller, modelName:String, modelClassRef:Class):void
		{ 		
			if(!controller)
			{
				throw new Error('registerModel Error: controller not registered!');				
			}		
			if(isModelRegistered(controller.getName(), modelName) )
			{	
				throw new Error('registerModel Error: model already registered: ' + modelName + ' modelClassRef:' +  getQualifiedClassName(modelClassRef));				
			}
			if(modelClassRef.NAME != modelName)
			{
				throw new Error('registerModel Error: name of modelClassRef did not match with modelName! The modelClassRef or modelName is wrong!: modelName: ' + modelName + ' modelClassRef:' +  getQualifiedClassName(modelClassRef));		
			}
			
			var model:Object =  new modelClassRef(modelName, controller);		
			if(model is Model && model is IModel)
			{
				ControllerVO(_controller[ controller.getName() ]).models[ modelName ] = model;
				
				//start dependency Injection   
				dependencyInjection(_controller[ controller.getName() ] as ControllerVO);
				
				//call registration method
				ControllerVO(_controller[ controller.getName() ]).models[ modelName ].registerHandler();
				
				//notify Application that model has registered
				controller.sendNotification(new NoteVO(Controller.MODEL_REGISTER, null, modelName, model) );
				
				log(controller.getName(), 'Model registered: ' +  modelName + ' ClassReference: ' + modelClassRef + ' to Controller: ' + controller.getName() );		
			}
			else
			{
				throw new Error('invalid modelClassRef!');
			}
		}
		
		/**
		 * removes a registered Model 
		 * @param controller
		 * @param modelName
		 * 
		 */      	
		public function removeModel(controller:Controller, modelName:String):void
		{
			if( ControllerVO(_controller[ controller.getName() ]).models[modelName] == null)
			{
				throw new Error('model not registered: ' + modelName);
			}  
			
			//notify Application before model is removed
			controller.sendNotification( new NoteVO(Controller.MODEL_REMOVE, null, modelName, ControllerVO(_controller[ controller.getName() ]).models[modelName]) );	
			
			//call removeHandler
			IModel(ControllerVO(_controller[ controller.getName() ]).models[modelName]).removeHandler();
			
			//clean up all dependencies...
			var instance:Object = ControllerVO(_controller[ controller.getName() ]).models[modelName];
			var controllerVo:ControllerVO = _controller[ controller.getName() ] as ControllerVO;
			
			//... in registered views
			for each (var viewInstance:Object in controllerVo.views)
			{
				//start dependency rejection
				if(viewInstance && viewInstance.constructor)
				{
					propertyRejectionOfInstance(viewInstance.constructor, controllerVo, instance);
				}
			}	
			//... in registered models
			for each (var modelInstance:Object in controllerVo.models)
			{
				//start dependency rejection
				if(modelInstance && modelInstance.constructor)
				{
					propertyRejectionOfInstance(modelInstance.constructor, controllerVo, instance);
				}
			}
			
			//remove instance
			ControllerVO(_controller[ controller.getName() ]).models[modelName] = null;
			delete ControllerVO(_controller[ controller.getName() ]).models[modelName]; 
			
			log(controller.getName(), 'Model removed: ' +  modelName );
		}
		
		/**
		 * retrieves a registered Model 
		 * @param controller
		 * @param modelName
		 * @return 
		 * 
		 */  	
		public function retrieveModel(controller:Controller, modelName:String):IModel
		{	
			if(ControllerVO(_controller[ controller.getName() ]).models[modelName] == null)
			{
				throw new Error('model not registered: ' + modelName);
			}
			return ControllerVO(_controller[ controller.getName() ]).models[modelName];
		}
		
		/**
		 * registers the given view instance
		 * @param controllerName
		 * @param viewName
		 * @param viewInstance
		 * 
		 */		
		public function registerView(controllerName:String, viewName:String, viewInstance:Object):void
		{
			if( isViewRegistered(controllerName, viewName) )
			{
				throw new Error('view already registered: ' + viewName);
			}
			
			//register Instance
			ControllerVO(_controller[ controllerName ]).views[ viewName ] = viewInstance;
			
			//start dependency Injection   
			dependencyInjection(_controller[ controllerName ] as ControllerVO);
			
			//notify Application that view has registered
			retrieveController(controllerName).sendNotification(new NoteVO(Controller.VIEW_REGISTER, null, viewName, viewInstance) );
			
			//log		
			log(controllerName, 'View registered: ' +  viewName + ' ViewInstance: ' + viewInstance );
		}
		
		/**
		 * removes a registered View 
		 * @param controllerName
		 * @param viewName
		 * 
		 */	
		public function removeView(controllerName:String, viewName:String):void
		{
			if( !isViewRegistered(controllerName, viewName) )
			{
				throw new Error( "view not registered!");
			}
			//notify Application before view is removed
			retrieveController(controllerName).sendNotification( new NoteVO(Controller.VIEW_REMOVE, null, viewName, ControllerVO(_controller[ controllerName ]).views[viewName]) );	
			
			//clean up all dependencies...
			var controllerVo:ControllerVO = _controller[ controllerName ] as ControllerVO;
			var instance:Object = controllerVo.views[viewName];	
			
			rejectAllPropertiesInInstance(instance.constructor, controllerVo, instance);
			
			//remove mapped view
			ControllerVO(_controller[ controllerName ]).views[viewName] = null;
			delete ControllerVO(_controller[ controllerName ]).views[viewName];
			
			//log
			log(controllerName, 'Removed: View registered with ViewName: "' +  viewName + ' to controller: ' + controllerName);	
		}
		
		/**
		 * retrieves the registered view
		 * @param controllerName
		 * @param viewName
		 * @return 
		 * 
		 */		
		public function retrieveView(controllerName:String, viewName:String):Object
		{
			if( !isViewRegistered(controllerName, viewName) )
			{
				throw new Error( "view not registered!");   	
			} 	
			return ControllerVO(_controller[ controllerName ]).views[ viewName ];
		}
		
		
		/**
		 * dependency Injection to all registered views and models
		 * @param controller
		 * 
		 */	
		private function dependencyInjection(controllerVo:ControllerVO):void
		{
			//views
			for each (var viewInstance:Object in controllerVo.views)
			{
				//start dependency injection
				if(viewInstance && viewInstance.constructor)
				{
					propertyInjection(viewInstance.constructor, controllerVo);
				}
			}
			
			//models
			for each (var modelInstance:Object in controllerVo.models)
			{
				//start dependency injection
				if(modelInstance && modelInstance.constructor)
				{
					propertyInjection(modelInstance.constructor, controllerVo);
				}
			}
			
		}
		
		/**
		 * returns annotation notes by given annotation string. For Example 'Inject' 
		 * @param variableNodes
		 * @param accessorNodes
		 * @param annotation
		 * @return 
		 * 
		 */	
		private function returnAnnotationNodes(variableNodes:XMLList, accessorNodes:XMLList, annotation:String=CompactMVC.ANNOTATION_INJECT):Array
		{
			var arr:Array = [];
			var metaData:XMLList;
			
			//find name in variables
			for each (var nodeXML:XML in variableNodes) 
			{
				metaData = nodeXML.metadata;
				if(metaData.length())
				{
					if(String(nodeXML.metadata.((@name==annotation)).@name).length > 0)
						arr.push(nodeXML);
				}       	
			}
			
			//find name in accessores    
			for each (nodeXML in accessorNodes) 
			{	
				metaData = nodeXML.metadata;
				if(metaData.length())
				{
					if(String(nodeXML.metadata.((@name==annotation)).@name).length > 0)
						arr.push(nodeXML);
				}       	
			}
			
			return arr;
		}
		
		/**
		 * rejects all properites in View
		 * @param classRef
		 * @param controllerVo
		 * @param viewInstance
		 * 
		 */	
		private function rejectAllPropertiesInInstance(classRef:Class, controllerVo:ControllerVO, targetInstance:Object):void
		{
			if(!classRef)
				throw new Error('classRef cannot be null!');
			
			if(!targetInstance)
				throw new Error('instance cannot be null!');
			
			var classRefXML:XML = describeType(classRef);
			var nodeXML:XML;
			var variableName:String;
			var variableTypeClassName:String;
			var targetClassName:String;
			var modelName:String;
			var foundNodes:Array = returnAnnotationNodes(classRefXML.factory.variable, classRefXML.factory.accessor);
			
			if(!foundNodes || foundNodes.length == 0)
				return;
			
			//start rejection       
			for each(nodeXML in foundNodes)
			{
				variableName = nodeXML.@name;
				variableTypeClassName = nodeXML.@type;
				targetClassName	= nodeXML.@declaredBy;
				
				//find view with wanted ClassName
				if(getQualifiedClassName(targetInstance) == targetClassName)
				{
					//reject model		    				 				
					targetInstance[variableName] = null;
					
					//log		
					log(controllerVo.controller.getName(), '## clean up Dependencies in Instance ## -> ApplicationController [' + getQualifiedClassName(controllerVo.controller) + '] targetInstance ['+targetInstance+'] targetType [' + targetClassName + '] variableName [' + variableName + '] variableType [' + variableTypeClassName + ']' );
				}	
			}
		}
		
		
		/**
		 * rejects the given property instance in all registered views and models (DI)
		 * @param classRef
		 * @param controllerVo
		 * @param instance
		 * 
		 */	
		private function propertyRejectionOfInstance(classRef:Class, controllerVo:ControllerVO, instance:Object):void
		{
			if(!classRef)
				throw new Error('classRef cannot be null!');
			
			if(!instance)
				throw new Error('instance cannot be null!');
			
			var instanceClassName:String = getQualifiedClassName(instance);
			
			var classRefXML:XML = describeType(classRef);
			//var methodNodes:XMLList = classRefXML.factory.method;
			var nodeXML:XML;
			var variableName:String;
			var variableTypeClassName:String;
			var targetClassName:String;
			var viewName:String;
			var modelName:String;
			var controllerVo:ControllerVO;
			var targetInstance:Object;
			var foundNodes:Array = returnAnnotationNodes(classRefXML.factory.variable, classRefXML.factory.accessor);
			
			if(!foundNodes || foundNodes.length == 0)
				return;
			
			//start rejection       
			for each(nodeXML in foundNodes)
			{
				variableName = nodeXML.@name;
				variableTypeClassName = nodeXML.@type;
				targetClassName	= nodeXML.@declaredBy;
				
				//iterate through the view list (registered views with controller)
				for (viewName in controllerVo.views)
				{
					//retrieve viewInstance
					targetInstance = retrieveView(controllerVo.controller.getName(), viewName);
					
					//find view with wanted ClassName
					if(getQualifiedClassName(targetInstance) == targetClassName && variableTypeClassName == instanceClassName)
					{
						//reject model		    				 				
						targetInstance[variableName] = null;
						
						//log		
						log(controllerVo.controller.getName(), '## view Dependency Rejection for Instance (' + instanceClassName + ') ## -> ApplicationController [' + getQualifiedClassName(controllerVo.controller) + '] targetInstance ['+targetInstance+'] targetType [' + targetClassName + '] variableName [' + variableName + '] variableType [' + variableTypeClassName + ']' );
					}
				}
				
				//iterate through the model list (registered models with controller)
				for (modelName in controllerVo.models)
				{				
					//retrieve model Instance
					targetInstance = retrieveModel(controllerVo.controller, modelName);
					
					//find view with wanted ClassName
					if(getQualifiedClassName(targetInstance) == targetClassName && variableTypeClassName == instanceClassName)
					{
						//reject model		    				 				
						targetInstance[variableName] = null;
						
						//log		
						log(controllerVo.controller.getName(), '## model Dependency Rejection for Instance (' + instanceClassName + ')## -> ApplicationController [' + getQualifiedClassName(controllerVo.controller) + '] targetInstance ['+targetInstance+'] targetType [' + targetClassName + '] variableName [' + variableName + '] variableType [' + variableTypeClassName + ']' );
					}
				}
				
			}
		}
		
		/**
		 * inject property instances having set metadata tag annotations to [Inject]
		 * @param myClass
		 * @param metadataTagName
		 * 
		 */	
		private function propertyInjection(classRef:Class, controllerVo:ControllerVO, commandInstance:ICommand=null):void
		{
			if(!classRef)
				throw new Error('classRef cannot be null!')
			
			var classRefXML:XML = describeType(classRef);
			//var methodNodes:XMLList = classRefXML.factory.method;
			var nodeXML:XML;
			var variableName:String;
			var variableTypeClassName:String;
			var targetClassName:String;
			var viewName:String;
			var controllerVo:ControllerVO;
			var modelName:String;
			var instanceToInject:Object;
			var targetInstance:Object
			var commandName:String;		
			var foundNodes:Array = returnAnnotationNodes(classRefXML.factory.variable, classRefXML.factory.accessor);
			
			
			if(!foundNodes || foundNodes.length == 0)
				return;
			
			//start injection       
			for each(nodeXML in foundNodes)
			{
				variableName = nodeXML.@name;
				variableTypeClassName = nodeXML.@type;
				targetClassName	= nodeXML.@declaredBy;
				
				//try modelinstance to inject
				instanceToInject = retrieveModelByClassName(variableTypeClassName);	
				
				/*
				* to do:
				* viewinstance injection will not work here, because there is no unique identifier accessible
				*/
				
				//try viewinstance to inject
				//if(!instanceToInject)
				//instanceToInject = retrieveViewByClassName(variableTypeClassName);	
				
				//break if instance is null
				if(!instanceToInject || !(instanceToInject is IModel))
					continue;
				
				//command
				if(commandInstance && commandInstance is ICommand)
				{			
					//find view with wanted ClassName				
					if(getDefinitionByName(getQualifiedClassName(commandInstance)) == classRef)
					{
						//inject model instance	    				 				
						commandInstance[variableName] = instanceToInject;
						
						//log		
						log(controllerVo.controller.getName(), '## command Dependency Injection ## -> ApplicationController [' + getQualifiedClassName( controllerVo.controller ) + '] targetInstance ['+commandInstance+'] targetType [' + targetClassName + '] variableName [' + variableName + '] variableType [' + variableTypeClassName + ']' );
					}
				}
				else
				{
					//iterate through the view list (registered views with controller)
					for (viewName in controllerVo.views)
					{
						//retrieve viewInstance
						targetInstance = retrieveView(controllerVo.controller.getName(), viewName);
						
						//find view with wanted ClassName
						if(getDefinitionByName(getQualifiedClassName(targetInstance)) == classRef)
						{
							//inject model		    				 				
							targetInstance[variableName] = instanceToInject;
							//log		
							log(controllerVo.controller.getName(), '## view Dependency Injection ## -> ApplicationController [' + getQualifiedClassName(controllerVo.controller) + '] targetInstance ['+targetInstance+'] targetType [' + targetClassName + '] variableName [' + variableName + '] variableType [' + variableTypeClassName + ']' );
						}
					}
					
					//iterate through the model list (registered models with controller)
					for (modelName in controllerVo.models)
					{
						//retrieve model Instance
						targetInstance = retrieveModel(controllerVo.controller, modelName);
						
						//find model with wanted ClassName
						if(getDefinitionByName(getQualifiedClassName(targetInstance)) == classRef)	    			
						{
							//inject model		    				 				
							targetInstance[variableName] = instanceToInject;
							//log		
							log(controllerVo.controller.getName(), '## model Dependency Injection ## -> ApplicationController [' + getQualifiedClassName(controllerVo.controller) + '] targetInstance ['+targetInstance+'] targetType [' + targetClassName + '] variableName [' + variableName + '] variableType [' + variableTypeClassName + ']' );
						}
					}
				}	    
			}
		}
		
		/**
		 * returns a registered model instance by  className, null if nothing found
		 * @param className
		 * @return 
		 * 
		 */	
		public function retrieveModelByClassName(className:String):IModel
		{	
			var controllerVo:ControllerVO;
			var modelInstance:IModel
			
			for each(controllerVo in _controller)
			{
				for each (modelInstance in controllerVo.models)
				{
					if(getQualifiedClassName(modelInstance) == className)
					{
						return modelInstance;
					}
				}
			}
			return null;
		}
		
		/**
		 *  returns a registered view instance by className, null if nothing found
		 * @param className
		 * @return 
		 * 
		 */	
		public function retrieveViewByClassName(className:String):DisplayObject
		{	
			var controllerVo:ControllerVO;
			var viewInstance:DisplayObject
			
			for each(controllerVo in _controller)
			{
				for each (viewInstance in controllerVo.views)
				{
					if(getQualifiedClassName(viewInstance) == className)
					{
						return viewInstance;
					}
				}
			}
			return null;
		}
		
		/**
		 * resets a controller 
		 * @param controllerName
		 * 
		 */	
		public function cleanup(controllerName:String):void
		{
			var controllerVo:ControllerVO = _controller[controllerName];
			
			//remove all views
			for (var viewKey:String in controllerVo.views)
			{
				removeView(controllerName, viewKey);
			}
			
			//remove all Models
			for (var modelKey:String in controllerVo.models)
			{
				removeModel(controllerVo.controller, modelKey);
			}
			//remove all Commands
			for (var commandKey:String in controllerVo.commands)
			{
				removeCommand(controllerVo.controller, commandKey);
			}
			
			//remove Controller
			removeController(controllerVo.controller);
			
			//notify Application
			CompactMVC.getInstance().sendJunctionNotification(Controller.CONTROLLER_REMOVE, controllerName);
			
			
			//log status
			logStatus(controllerName, controllerVo);
		}
		
		/**
		 * checks whether a controller is present 
		 * @param controllerName
		 * @return 
		 * 
		 */		
		public function isControllerRegistered(controllerName:String):Boolean
		{
			if(_controller[ controllerName ] != null)
			{
				return true;
			}
			return false;
		}
		
		/**
		 * checks whether a model is present  
		 * @param controllerName
		 * @param modelName
		 * @return 
		 * 
		 */		
		public function isModelRegistered(controllerName:String, modelName:String):Boolean
		{
			if( isControllerRegistered(controllerName) )
			{
				if(ControllerVO(_controller[ controllerName ]).models[ modelName ] != null)
				{
					return true;
				}
			}			
			return false;
		}
		
		/**
		 * checks whether a view is present  
		 * @param controllerName
		 * @param viewName
		 * @return 
		 * 
		 */		
		public function isViewRegistered(controllerName:String, viewName:String):Boolean
		{
			if( isControllerRegistered(controllerName) )
			{
				if(ControllerVO(_controller[ controllerName ]).views[viewName] != null)
				{
					return true;
				}
			}
			return false;
		}
		
		/**
		 * retrieves all registered Views of given controller 
		 * @param controllerName
		 * @return 
		 * 
		 */	
		public function retrieveAllRegisteredViews(controllerName:String):Array
		{
			if( isControllerRegistered(controllerName) )
			{
				if(ControllerVO(_controller[ controllerName ]).views != null)
				{
					return ControllerVO(_controller[ controllerName ]).views;
				}
			}
			return null;
		}
		
		
		
		/**
		 * trace log message 
		 * @param controllerName
		 * @param message
		 * 
		 */	
		private function log(controllerName:String, message:String):void
		{
			if(!isControllerRegistered(controllerName))
				return;
			
			switch( ControllerVO(_controller[ controllerName ]).controller.logLevel)
			{
				case CompactMVC.LOGLEVEL_ALL:		
					logStatus(controllerName, ControllerVO(_controller[ controllerName ]));
				case CompactMVC.LOGLEVEL_DEBUG:
					logMessage(ControllerVO(_controller[ controllerName ]).controller, message);
			}
		}
		
		/**
		 * trace log message
		 * @param controller
		 * @param message
		 * 
		 */	
		private function logMessage(controller:Controller, message:String):void
		{	
			var msg:String = currentTime + ' ' + message;	
			if(controller.logFunction != null)
				controller.logFunction(message);
			else
				trace(msg);
		}
		
		/**
		 * trace the controller status 
		 * @param controllerName
		 * @param controllerVO
		 * 
		 */	
		private function logStatus(controllerName:String, controllerVO:ControllerVO):void
		{
			logMessage(controllerVO.controller, '');
			logMessage(controllerVO.controller, '[ -- DETAILED LOG START -- ]');
			logMessage(controllerVO.controller, '[ -- Controller Name: ' + controllerName + '	[' + getQualifiedClassName(controllerVO.controller)  + ']');
			
			//list commands
			logMessage(controllerVO.controller, '     Commands ------------------------->');
			for (var commandKey:String in controllerVO.commands)
			{
				logMessage(controllerVO.controller, '	Notification Type: '+ commandKey + '	[' + getQualifiedClassName( controllerVO.commands[commandKey]) + ']'); 
			}
			
			//list models
			logMessage(controllerVO.controller, '     Models --------------------------->');
			for (var modelKey:String in controllerVO.models)
			{
				logMessage(controllerVO.controller, '	Model Name: ' + modelKey + '	[' +  getQualifiedClassName( controllerVO.models[modelKey]) + ']' ); 
			}
			
			//list views
			logMessage(controllerVO.controller, '     Views ---------------------------->');
			for (var viewKey:String in controllerVO.views)
			{
				logMessage(controllerVO.controller, '	View Name: ' + viewKey + ' [' + getQualifiedClassName( controllerVO.views[viewKey]) + ']' );
			}
			
			logMessage(controllerVO.controller, ']');		
			logMessage(controllerVO.controller, '[ -- DETAILED LOG STOP -- ]');
			logMessage(controllerVO.controller, '');
		}
		
		/**
		 * Creates a String of valid time value
		 * @return 			String 		current time as a String using valid hours, minutes, seconds and milliseconds
		 */	 
		private function get currentTime ():String
		{
			var date:Date = new Date();
			return "CompactMVC " + fixTime(date.getHours()) + ':' + fixTime(date.getMinutes()) + ':' + fixTime(date.getSeconds()) + '.' + fixTime(date.getMilliseconds());
		}
		
		private function fixTime(value:Number):String
		{
			return value > 9 ? value.toString() : "0" + value.toString();
		}
	}
}